from collections import defaultdict
import time
import numpy as np
from django.core.paginator import Paginator
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from .models import Book
from django.core.cache import cache
from functools import lru_cache
import threading

cache_lock = threading.Lock()

@lru_cache(maxsize=1000)
def calculate_user_vector(book):
    """计算用户向量"""
    try:
        return np.array([
            float(book.star if book.star is not None else 0) / 100.0,  # 评分
            np.log1p(float(book.comment_num if book.comment_num is not None else 0)) / 10.0,  # 评论数
            float(book.discount if book.discount is not None else 0) / 100.0  # 折扣
        ])
    except Exception as e:
        print(f"向量计算错误 ({book.title}): {str(e)}")
        return np.zeros(3)

def create_interaction_matrix(books):
    """创建交互矩阵"""
    n_books = len(books)
    matrix = np.zeros((n_books, 3))  # [评分,评论数,折扣]

    for i, book in enumerate(books):
        matrix[i] = calculate_user_vector(book)

    return matrix

def calculate_similarity(matrix):
    """计算相似度矩阵"""
    # 使用余弦相似度
    similarity = np.zeros((matrix.shape[0], matrix.shape[0]))

    # 计算每个向量的范数
    norms = np.linalg.norm(matrix, axis=1)
    norms[norms == 0] = 1  # 避免除零

    # 计算相似度
    similarity = np.dot(matrix, matrix.T) / np.outer(norms, norms)

    return similarity

def get_top_similar_items(similarity_matrix, item_index, n=10):
    """获取最相似的项目"""
    similar_scores = similarity_matrix[item_index]
    similar_items = np.argsort(similar_scores)[-n-1:][::-1]
    # 排除自身
    similar_items = similar_items[similar_items != item_index]
    return similar_items[:n]

def calculate_recommendation_scores(books, similarity_matrix, interaction_matrix):
    """计算推荐分数"""
    n_books = len(books)
    scores = np.zeros(n_books)

    for i in range(n_books):
        # 获取相似项目
        similar_items = get_top_similar_items(similarity_matrix, i)

        # 基础分数（考虑评分、评论数和折扣）
        base_score = (interaction_matrix[i][0] * 0.4 +  # 评分权重
                     interaction_matrix[i][1] * 0.4 +  # 评论数权重
                     interaction_matrix[i][2] * 0.2)   # 折扣权重

        # 相似项目的影响
        if len(similar_items) > 0:
            similar_scores = np.mean([interaction_matrix[j][0] for j in similar_items])
            scores[i] = base_score * (1 + similar_scores)
        else:
            scores[i] = base_score

    return scores

def get_recommended_books(books):
    """协同过滤推荐算法"""
    try:
        if not books:
            return []

        # 使用缓存
        cache_key = 'collaborative_recommended_books'
        with cache_lock:
            cached_result = cache.get(cache_key)
            if cached_result:
                return cached_result

        # 1. 创建交互矩阵
        interaction_matrix = create_interaction_matrix(books)

        # 2. 计算相似度矩阵
        similarity_matrix = calculate_similarity(interaction_matrix)

        # 3. 计算推荐分数
        scores = calculate_recommendation_scores(books, similarity_matrix, interaction_matrix)

        # 4. 排序并返回推荐结果
        sorted_indices = np.argsort(scores)[::-1]
        recommended_books = [books[i] for i in sorted_indices]

        # 缓存结果
        with cache_lock:
            cache.set(cache_key, recommended_books, 3600)  # 缓存1小时

        return recommended_books

    except Exception as e:
        print(f"推荐算法错误: {str(e)}")
        return sorted(books, key=lambda x: (float(x.star if x.star else 0),
                                          float(x.comment_num if x.comment_num else 0)),
                     reverse=True)

@login_required(login_url='/login/')
def recommend(request):
    """推荐视图函数"""
    start_time = time.time()

    try:
        # 使用缓存
        cache_key = 'all_books_recommendation'
        with cache_lock:
            recommended_books = cache.get(cache_key)

        if recommended_books is None:
            # 获取所有书籍
            all_books = list(Book.objects.all()
                           .order_by('-star', '-comment_num')
                           .select_related())

            # 计算推荐
            recommended_books = get_recommended_books(all_books)

            # 缓存结果
            with cache_lock:
                cache.set(cache_key, recommended_books, 3600)

        # 分页处理
        paginator = Paginator(recommended_books, 18)
        page_number = request.GET.get('page', 1)

        try:
            page_number = int(page_number)
            books = paginator.page(page_number)
        except:
            books = paginator.page(1)

        process_time = time.time() - start_time

        return render(request, 'system/recommend.html', context={
            'books': books,
            'page': page_number,
            'process_time': round(process_time, 2),
            'total_books': len(recommended_books),
            'total_pages': paginator.num_pages
        })

    except Exception as e:
        print(f"推荐系统错误: {str(e)}")
        return render(request, 'system/recommend.html', {
            'books': [],
            'error_message': '推荐系统暂时不可用'
        })
